package edu.umn.ftpmaster;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.sql.*;

public class Master {
	
	private List <Event> events = new ArrayList<Event>();
	private int EventCount;
	private int firstPort = 5005;
	private int masterPort = 5000;
	private ServerSocket master=null;
	
	public Master(){
		try {
			master = new ServerSocket(masterPort);
		} catch (IOException e) {
			e.printStackTrace();
		}

		/* Viswanath: Crash recovery code */
		
		Connection db = null;
                Statement sql;
                try {
                        db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                }
                catch(SQLException e){
                        System.out.println("Connection Failed! Check output console");
                        e.printStackTrace();
                }

                if(db != null){
                        try{
                                sql = db.createStatement();
				ResultSet rset = sql.executeQuery("select max(eventid) from event;");				
				rset.next();
				EventCount = rset.getInt(1) + 1;
				System.out.println("Event count is initialized to "+ EventCount);
			}
			catch(SQLException e){
				System.out.println("Exception in Constructor of Master");
				System.out.println(e);
			}
		}
		else{System.out.println("Connection failed in Constructor of the Master");} 

		/* Populate the event data structures after the crash */
		populate_data_structures();
	}
	/**
	 * start the master
   	 */
	public void masterStart()
	{
		
		while(true)
		{
			System.out.println("master started");
			try {
				new dealwithit(master.accept()).start();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}


/**
 * thread that deals with the incoming message
 * @author Chonglei
 *
 */
private class dealwithit extends Thread{
	private Socket socket = null;
	
	private dealwithit(Socket socket1){
		super("dealwithit");
		this.socket = socket1;
	}
		
	public void run(){
		try{
				
			System.out.println("received a connection");
			
			ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
			ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
			Object data =null;
	
			//while(socket.isConnected()){
				if((data = ois.readObject()) != null)
				{
					String option = ((Command)data).getOption();

					if(option.equals("connect")){//first come    //viswanath: not sure about this option 
						//Location ll = ((Command)data).getLocation();
						//List<Event> usefulEvents=ListEvent(ll);
						//to do
						//send back the available events by location
						//oos.writeObject(usefulEvents);
						
						//System.out.println("received new request");
					}
					else if(option.equals("new"))
					{//create a new event
						Location_stp ll = ((Command)data).getLocation();
						String eventName = ((Command)data).getName();
						String event_creator = ((Command)data).getCreator();
						String laddress = ((Command)data).getLaddress();

						System.out.println("Received a connection for new with params "+eventName+" "+event_creator+" "+laddress);  
                                        	Event newEvent=addEvent(ll,eventName,event_creator,laddress);   
						//newEvent.setScope(radius);             						
						Account account=addNewUser(newEvent,true,event_creator);
						
						//create response  
						Command cc = new Command("account",null,null,eventName+","+String.valueOf(newEvent.port)+","+account.username+","+account.password,null,null);
						//send response
						oos.writeObject(cc);
						
						System.out.println("done with new");
					}
					else if(option.equals("join"))
					{//join the one of the current event
						String eventName = ((Command)data).getName();
						Event event = findEventByName(eventName);
						String pname = ((Command)data).getCreator(); //Here getCreator will return the person joining 	
						System.out.println("Received a connection for join with params"+eventName+" "+pname);						
						//add new account and new client
						Account account = addNewUser(event,false,pname);						
						//create response   //viswanath response should contain the port number of the server 
						Command cc = new Command("account",null,null,eventName+","+String.valueOf(event.port)+","+account.username+","+account.password,null,null);
						//send response
						oos.writeObject(cc);
					}
					else if(option.equals("search_loc"))
					{
						//This option searches the event by performing a range query on the query location 
						Location_stp ll = ((Command)data).getLocation();						
						System.out.println("received a search_loc with params "+ ll.latitude +" "+ ll.longitude+" "+ ll.time);
						String res = Search_near_events(ll);
						
						//Create respone 
						Command cc = new Command("results",null,null,res,null,null);
						//send response	
						oos.writeObject(cc);
					}
					else if(option.equals("search_adv"))
					{	
						Location_stp ll = ((Command)data).getLocation();
						String p1 = ((Command)data).getName();
						String p2 = ((Command)data).getMessage();
						if(p1!=null) p1 = p1.trim();
						if(p2!=null) p2 = p2.trim();			
						
						System.out.println("received an advanced search with params "+ll.latitude+" "+ll.longitude+" "+p1+" "+p2);
						String res = Advanced_search(ll,p1,p2);
						Command cc = new Command("results",null,null,res,null,null);
						oos.writeObject(cc);
					}
				}
				socket.close();	
			//}
		}
		catch(IOException e){
			e.printStackTrace();
		}
		catch (ClassNotFoundException e) {
                        e.printStackTrace();
                }

	}
	/**
	 * add a new event 
	 */
	public Event addEvent(Location_stp ll, String eventName, String creator, String laddress)
	{
		String dirName = "event"+EventCount;	
		Event event = new Event(ll,eventName,dirName,firstPort+EventCount,EventCount);
		
		EventCount++; //Viswanath: EventCount is initialized from the database

		events.add(event);
		event.add_jdbc_event(creator,laddress,EventCount,eventName,ll,firstPort+EventCount-1,dirName);
		return event;
	}
	/**
	 * list the available events based on the incoming location
	 */
	public List<Event> ListEvent(Location_stp ll)
	{
		//to do
		//list the available events and return to  the user
		List<Event> currentEvents = new ArrayList<Event>();
		for(Iterator<Event> it = events.iterator();it.hasNext();){
			Event currentEvent = it.next();
			if(util.distance(currentEvent.locl, ll)<it.next().radius){
				currentEvents.add(currentEvent);
			}
		}
		return currentEvents;
	}
	
	/**
	 * add a new user into the corresponding event
	 * @param event: the event that will add a new user
	 * @return : the generated account
	 */
	public Account addNewUser(Event event,boolean creat, String pname)                   //in case he is creator, his name would already be there
	{
		Account account = new Account();
		account.username = event.getName()+event.count;					//username
		event.count++;
		account.password = String.valueOf(System.currentTimeMillis());	                //password
		
		//add a new client //viswanath: not sure about this ???
		//event.addClient(account,addr);
		
		//add a new account to the FTP server
		event.server.addAccount(account);

		if(creat == true)  //user already present just add the password and his user name
		{
			Connection db = null;
                	Statement sql;        
                	try {
                        	db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                	}	
                	catch(SQLException e){
                        	System.out.println("Connection Failed! Check output console");
                       		e.printStackTrace();
                	}
			
			if(db != null){
				try{
					sql = db.createStatement();
	    String sqltext = "update person set username = '"+account.username+"',"+"password = '"+account.password+"' where person_name like '"+pname+"';"; 
					sql.executeUpdate(sqltext);		
					db.close();
				}
				catch(SQLException e){
					System.out.println("Exception in function addNewUser");
					System.out.println(e);
				}
			}
			else{
				System.out.println("Failed to make a connection in Master.java function AddNewUser");
			}
		}
		else
		{
			Connection db = null;
			Statement sql;
			try{
				db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                        }
                        catch(SQLException e){
                                System.out.println("Connection Failed! Check output console");
                                e.printStackTrace();
                        }

                        if(db != null){
				try{
                                	sql = db.createStatement();
	      				ResultSet results = sql.executeQuery("select count(*) from person;");
                                	results.next();
                                	int new_pid = results.getInt(1);
                                	new_pid++;
					db.close();	
					
					db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow");	
             				sql = db.createStatement();
				String sqltext = "insert into person values ('"+pname+"',"+new_pid+",'"+account.username+"','"+account.password+"');";
                              		sql.executeUpdate(sqltext);
					db.close();

			        	/*Adding the data to the people_event table as well*/ 
					db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow");
                                        sql = db.createStatement();
                                        sqltext = "insert into people_event values ("+new_pid+","+event.eventid+");";
                                        sql.executeUpdate(sqltext);
                                        db.close();
			
		
				}
				catch(SQLException e){
					System.out.println("Exception in function addNewUser");
					System.out.println(e);
				}			
			}
		}
		//to do: send the generated account to the mobile user
		return account;
	}
	private Event findEventByName(String name){
		  for(Iterator<Event>it= events.iterator();it.hasNext();){   
		       Event event =it.next();
		       if(event.getName().equals(name)){
		    	   return event;
		       }
		   }
		  return null;
	}


	public String Search_near_events(Location_stp ll)
	{	
		String res = "";
		double scale_down_time = (double)ll.time/(double)3600000.0;
		
		Connection db = null;
                Statement sql;                //Our SQL statement
                try {
                	db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                }
                catch(SQLException e){
                	System.out.println("Connection Failed! Check output console");
                        e.printStackTrace();
                }

                if(db != null){
                	
			try{
				sql = db.createStatement();

 				ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ST_DWithin(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("
				+ll.latitude+" "+ll.longitude+" "+scale_down_time+")'), 0.009) and abs(time-"+ll.time+") < 72000000 order by ST_distance(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("
                        	+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) asc;");

				while(rset.next())
				{
					int numColumns = rset.getMetaData().getColumnCount();
            				for ( int i = 1 ; i <= numColumns ; i++ ) {
              				 	res = res+rset.getObject(i)+"|";
            				}
					res = res + ":";	
				}
                        	db.close();
			}
			catch(SQLException e){
				System.out.println("Exception in Search_near_events function Master.java");
				System.out.println(e);
			}
                }
                else{
                	System.out.println("Failed to make a connection in Master.java function AddNewUser");
		}	
			
		return res;
	}	

	public String Advanced_search(Location_stp ll, String p1, String p2)
	{
		String res = "";
                double scale_down_time = (double)ll.time/(double)3600000.0;
		double radius = -1;

		if(p2!=null){
			radius = Double.parseDouble(p2);
			radius = radius * 1609.344;
		}

                Connection db = null;
                Statement sql;                //Our SQL statement
                try {
                        db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                }
                catch(SQLException e){
                        System.out.println("Connection Failed! Check output console");
                        e.printStackTrace();
                }

                if(db != null){

                        try{
				if(ll.latitude != 0.0 && p1 !=null) //events around a certain location with a certain name
				{
					if(radius==-1)					
					{
						db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                                        	sql = db.createStatement();
                                        	ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ST_DWithin(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")'), 0.009) and e.ename like '%"
+p1+"%'order by ST_distance(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) asc;");
					
                                        	while(rset.next())
                                        	{
                                                	int numColumns = rset.getMetaData().getColumnCount();
                                                	for ( int i = 1 ; i <= numColumns ; i++ ) {
                                                        	res = res+rset.getObject(i)+"|";
                                                	}
                                                	res = res + ":";
                                        	}
                                        	db.close();
                                        	return res;
					}
					if(radius!=-1)
					{
						db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                                                sql = db.createStatement();
                                                ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ST_Distance_Sphere(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) <"+radius+"and e.ename like '%"+p1+"%'order by ST_distance(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) asc;");
                                        
                                                while(rset.next())
                                                {
                                                        int numColumns = rset.getMetaData().getColumnCount();
                                                        for ( int i = 1 ; i <= numColumns ; i++ ) {
                                                                res = res+rset.getObject(i)+"|";
                                                        }
                                                        res = res + ":";
                                                }
                                                db.close();
                                                return res;
					}
				}
				else if(ll.latitude != 0.0 && p1==null) //All events at a certain location irrespective of time 
				{
					if(radius==-1)
					{
						db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
						sql = db.createStatement();
                                		ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ST_DWithin(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")'), 0.009) order by ST_distance(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) asc;");
					
						while(rset.next())
                                		{
                                        		int numColumns = rset.getMetaData().getColumnCount();
                                        		for ( int i = 1 ; i <= numColumns ; i++ ) {
						    		res = res+rset.getObject(i)+"|";
                                        		}
                                        		res = res + ":";
      						}
                                		db.close();
						return res;
					}
					if(radius!=-1)
					{
					db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                                                sql = db.createStatement();
                                                ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ST_Distance_Sphere(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) < "+radius+ "order by ST_distance(e.stpoint, ST_GeomFromEWKT('SRID = -1;POINT("+ll.latitude+" "+ll.longitude+" "+scale_down_time+")')) asc;");

                                                while(rset.next())
                                                {
                                                        int numColumns = rset.getMetaData().getColumnCount();
                                                        for ( int i = 1 ; i <= numColumns ; i++ ) {
                                                                res = res+rset.getObject(i)+"|";
                                                        }
                                                        res = res + ":";
                                                }
                                                db.close();
                                                return res;			
					}
				}
				else if(p1!=null && ll.latitude == 0.0) //all events with name like 
				{	
					
					System.out.println("p1 is"+ p1);
					db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
					sql = db.createStatement();
					System.out.println("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ename like '%"+p1+"%';");
					ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator and ename like '%"+p1+"%';");
                                        while(rset.next())
                                        {
                                                int numColumns = rset.getMetaData().getColumnCount();
                                                for ( int i = 1 ; i <= numColumns ; i++ ) {
                                                        res = res+rset.getObject(i)+"|";
                                                }
                                                res = res + ":";
                                        }
                                        db.close();
                                        return res;
				}
				else //return all the events
				{
					db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
					sql = db.createStatement();	
		      ResultSet rset = sql.executeQuery("select e.ename, p.person_name, e.location from event e, person p  where p.person_id = e.creator;");			
					while(rset.next())
                                        {
                                                int numColumns = rset.getMetaData().getColumnCount();
                                                for ( int i = 1 ; i <= numColumns ; i++ ) {
                                                        res = res+rset.getObject(i)+"|";
                                                }
                                                res = res + ":";
                                        }
                                        db.close();
                                        return res;
				}
                        }
                        catch(SQLException e){
                                System.out.println("Exception in Search_near_events function Master.java");
                                System.out.println(e);
                        }
                }
                else{
                        System.out.println("Failed to make a connection in Master.java function AddNewUser");
                }

		return res;
	}
}



	public void populate_data_structures()
	{
		
		Connection db = null;
                Statement sql;                //Our SQL statement
                try {
                        db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                }
                catch(SQLException e){
                        System.out.println("Connection Failed! Check output console");
                        e.printStackTrace();
                }

                if(db != null){
			try{
				/* Creates the event objects and populates the events array list; FTP Servers are created in Event class Constructor */
				sql = db.createStatement();
				ResultSet rset = sql.executeQuery("select eventid, ST_AsEWKT(stpoint), creator, ename, port, dir_path from event;");

				while(rset.next())
				{
					String toparse = rset.getString(2);
					String[] tokens = toparse.split("\\(");
					String[] tokens1 = tokens[1].split(" ");
					double lat = Double.parseDouble(tokens1[0]);
					double longt = Double.parseDouble(tokens1[1]);
					String[] tokens2 = tokens1[2].split("\\)");
					long time = (long)(Double.parseDouble(tokens2[0]) * 3600000);					

					Location_stp temp = new Location_stp();
					temp.latitude = lat ;
					temp.longitude = longt;
					temp.time = time;
			
					Event etemp = new Event(temp, rset.getString(4), rset.getString(6), rset.getInt(5), rset.getInt(1));
					events.add(etemp);					
				}

				/* Initializes the user count in the event object, required for username creation */
				for(Iterator<Event>it= events.iterator();it.hasNext();){
                                        Event event =it.next();

                                        db.close();
                                        db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                                        sql = db.createStatement();
                  			String sqltext = "select count(*) from people_event pe where pe.eid = "+event.eventid+";";
                                        ResultSet rs = sql.executeQuery(sqltext);
                                        rs.next();
                                       	event.count = event.count + rs.getInt(1); 
                                }
	
				/*Creates populates the FTPServer of each event with their corresponding users.*/
				for(Iterator<Event>it= events.iterator();it.hasNext();){
                       			Event event =it.next();	
					
					db.close();
                                	db = DriverManager.getConnection("jdbc:postgresql://127.0.0.1/peacock","stfs", "sparrow"); //the port number was 5432
                                	sql = db.createStatement();
		  String sqltext = "select p.username, p.password from person p, people_event pe where p.person_id = pe.pid and pe.eid = "+event.eventid+";";
					ResultSet rs = sql.executeQuery(sqltext);				
			                while(rs.next()){  			
						Account atemp = new Account();
						atemp.username = rs.getString(1);
						atemp.password = rs.getString(2);
						event.server.addAccount(atemp);
					}
					//event.server.startServer();	
                   		}

			}
			catch(SQLException e){
				System.out.println("SQL Exception in populate data structures");
				System.out.println(e);	
			}
		}
		else{
			System.out.println("Failed to make connection in the populate data strucures function");
		}
	}
}
